<?php

/**
 * 桥接（桥梁/柄体/接口）模式（处理多维度变化）
 * 
 * 概述：将抽象部分与它的实现部分分离，使它们都可以独立地变化。
 * 
 * 如果软件系统中某个类存在两个独立变化的维度，通过该模式可以将这两个维度分离出来，使两者可以独立扩展，
 * 让系统更加符合“单一职责原则”。与多层继承方案不同，它将两个独立变化的维度设计为两个独立的继承等级结构，
 * 并且在抽象层建立一个抽象关联，该关联关系类似一条连接两个独立继承结构的桥，故名桥接模式。
 * 
 * 类型：结构型模式
 * 
 * 角色：
 * 	1，抽象类。定义抽象类的接口并保存一个对实现化对象的引用。
 *  2，扩充抽象类。
 *  3，实现类。
 *  4，具体实现类
 * 
 * 优点：
 *  1，分离抽象接口及其实现部分。桥接模式使用“对象间的关联关系”解耦了抽象和实现之间固有的绑定关系，使得抽象
 *  和实现可以沿着各自的维度来变化。所谓抽象和实现沿着各自维度的变化，也就是说抽象和实现不再在同一个继承层次
 *  结构中，而是“子类化”它们，使它们各自都具有自己的子类，以便任何组合子类，从而获得多维度组合对象。
 *  2，在很多情况下，桥接模式可以取代多层继承方案，多层继承方案违背了“单一职责原则”，复用性较差，且类的个数
 *  非常多，桥接模式是比多层继承方案更好的解决方法，它极大减少了子类的个数。
 *  3，桥接模式提高了系统的可扩展性，在两个变化维度中任意扩展一个维度，都不需要修改原有系统，符合“开闭原则”。
 *  
 * 缺点：
 *  1，桥接模式的使用会增加系统的理解与设计难度，由于关联关系建立在抽象层，
 *  要求开发者一开始就针对抽象层进行设计与编程。
 *  2，桥接模式要求正确识别出系统中两个独立变化的维度，因此其使用范围具有一定的局限性，
 *  如何正确识别两个独立维度也需要一定的经验积累。
 *  
 * 适用场景：
 *  1，如果一个系统需要在抽象化和具体化之间增加更多的灵活性，避免在两个层次之间建立静态的继承关系，
 *  通过桥接模式可以使它们在抽象层建立一个关联关系。
 *  2，“抽象部分”和“实现部分”可以以继承的方式独立扩展而互不影响，在程序运行时可以动态将一个抽象化子类的对象
 *  和一个实现化子类的对象进行组合，即系统需要对抽象化角色和实现化角色进行动态耦合。
 *  3，一个类存在两个（或多个）独立变化的维度，且这两个（或多个）维度都需要独立进行扩展。
 *  4，对于那些不希望使用继承或因为多层继承导致系统类的个数急剧增加的系统，桥接模式尤为适用。
 */

/**
 * 抽象类
 */
abstract class Abstraction
{
	protected $implement;
	
	public function setImplement(Implementor $impl)
	{
		$this->implement = $impl;
	}
	
	public abstract function operation();
}

/**
 * 扩充抽象类
 */
class RefinedAbstraction extends Abstraction
{
	public function operation()
	{
		echo '抽象类操作。<br/>';
		$this->implement->operationImpl();
	}
}

/**
 * 实现类
 */
interface Implementor
{
	public function operationImpl();
}

/**
 * 具体实现类A
 */
class ConcreteImplementorA implements Implementor
{
	public function operationImpl()
	{
		echo '具体实现类A操作。<br />';
	}
}

/**
 * 具体实现类B
 */
class ConcreteImplementorB implements Implementor
{
	public function operationImpl()
	{
		echo '具体实现类B操作。<br />';
	}
}

class Client
{
	public static function main()
	{
		$abstraction = new RefinedAbstraction();
		
		$implement = new ConcreteImplementorA();
		$abstraction->setImplement($implement);
		$abstraction->operation();
		
		$implement = new ConcreteImplementorB();
		$abstraction->setImplement($implement);
		$abstraction->operation();
	}
}

Client::main();